package org.bitvision.rlepacker;


import org.apache.commons.lang3.ArrayUtils;

import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;


public class RlePacker {

    public static void main(String args[]) throws Exception {
        if (args.length!=1) {
            System.out.println("RlePacker <input binary file>");
            System.exit(1);
        }

        byte[] fileBytes = Files.readAllBytes(Paths.get(args[0]));
        Map<Byte,Integer> stadistics = new HashMap<Byte,Integer>();

        for (byte data : fileBytes) {
            if (stadistics.containsKey(data)) {
                stadistics.put(data, stadistics.get(data) + 1);
            } else {
                stadistics.put(data , 1);
            }
        }

        List<Byte> keysList = new ArrayList<Byte>(stadistics.keySet());
        Collections.sort(keysList);
        List<String> keysListString = new ArrayList<String>();
        for (Byte value : keysList) {
            keysListString.add(String.format("%02X",value & 0xFF));
        }

        byte tokenCandidate = 0;
        boolean found = false;
        for (byte b=0; b<=255 ; b++) {
            //System.out.println(String.format("%02X",b & 0xFF));
            if (!keysListString.contains(String.format("%02X",b & 0xFF))) {
                tokenCandidate = b;
                found = true;
                break;
            }
        }

        if (found) {
            System.out.println("Token will be :" + String.format("%02X",tokenCandidate & 0xFF));
        } else {
            System.out.println("Token not found!");
            System.exit(1);
        }

        ///compression

        //PrintWriter out = new PrintWriter(args[0]+".rle");

        //int j = 0;
        List<Byte> outputBuffer = new ArrayList<Byte>();
        outputBuffer.add(tokenCandidate);   //first byte it's counter token

        for (int i = 0; i < fileBytes.length; i++) {
            int runLength = 1;
            while (i+1 < fileBytes.length && fileBytes[i] == fileBytes[i+1] && runLength<256) {
                runLength++;
                i++;
            }

            if (runLength>3 ) { //compress
                outputBuffer.add(tokenCandidate); //token
                outputBuffer.add(fileBytes[i]); //value
                outputBuffer.add(((byte) runLength)); //counter
            } else { //not compression
                for (int c = 0 ; c<runLength ; c++) {
                    outputBuffer.add(fileBytes[i]);
                }
            }

        }


        outputBuffer.add(tokenCandidate);   //last two bytes are counter token twice
        outputBuffer.add(tokenCandidate);
        Byte[] finalbuffer = outputBuffer.toArray(new Byte[outputBuffer.size()]);
        byte[] finalarray = ArrayUtils.toPrimitive(finalbuffer);
        Path path = Paths.get(args[0]+".rle");
        Files.write(path, finalarray);
        System.out.println("Writing " + args[0]+".rle");

    }


}
